﻿
namespace AutoWiring.Common
{
    /// <summary>
    /// Revit点类
    /// </summary>
    public class XYZ
    {
        
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public static XYZ Zero { get { return new XYZ(0,0,0); } }
        public static XYZ BaseX { get { return new XYZ(1,0,0); } }
        public static XYZ BaseY { get { return new XYZ(0,1,0); } }
        public static XYZ BaseZ { get { return new XYZ(0,0,1); } }

        
        // 运算符重载用于向量运算
        public static XYZ operator -(XYZ a, XYZ b) => new XYZ(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
        public static XYZ operator +(XYZ a, XYZ b) => new XYZ(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
        public static XYZ operator *(XYZ a, double scalar) => new XYZ(a.X * scalar, a.Y * scalar, a.Z * scalar);
        public static XYZ operator /(XYZ a, double scalar) => new XYZ(a.X / scalar, a.Y / scalar, a.Z / scalar);

        
        /// <summary>
        /// 点坐标相加
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public XYZ Add(XYZ source)
        {
            return new XYZ(X + source.X, Y + source.Y, Z + source.Z);
        }

        /// <summary>
        /// 返回两点之间的距离值
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public double DistanceTo(XYZ source)
        {          
            return Math.Sqrt(Math.Pow(X - source.X, 2) + Math.Pow(Y - source.Y, 2) + Math.Pow(Z - source.Z, 2));
        }

        /// <summary>
        /// 返回单位向量
        /// </summary>
        /// <returns></returns>
        public XYZ Normalize()
        {
            double length = Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2) + Math.Pow(Z, 2));
            return new XYZ(X*1.0/ length, Y*1.0 / length, Z*1.0 / length);
        }

        /// <summary>
        /// 返回向量长度倍数的向量
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public XYZ Multiply(double value)
        {
            return new XYZ(X * value, Y * value, Z * value);
        }

        /// <summary>
        /// 返回两个各个向量的夹角
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public double AngleTo(XYZ source)
        {        
            return Math.Acos(this.DotProduct(source)/ (this.Length() * source.Length()));
        }

        /// <summary>
        /// 绕着某向量旋转指定角度得到新向量
        /// </summary>
        /// <param name="rotationAxis">旋转轴向量</param>
        /// <param name="angleInRadians">旋转角度</param>
        /// <returns></returns>
        public XYZ AngleByAxis(XYZ rotationAxis, double angleInRadians)
        {

            XYZ origXYZ = new XYZ(X, Y, Z);
            // 计算旋转矩阵
            double c = Math.Cos(angleInRadians);
            double s = Math.Sin(angleInRadians);
            double t = 1 - c;

            double x = origXYZ.X * (rotationAxis.X * rotationAxis.X * t + c) +
                       origXYZ.Y * (rotationAxis.X * rotationAxis.Y * t - rotationAxis.Z * s) +
                       origXYZ.Z * (rotationAxis.X * rotationAxis.Z * t + rotationAxis.Y * s);

            double y = origXYZ.X * (rotationAxis.Y * rotationAxis.X * t + rotationAxis.Z * s) +
                       origXYZ.Y * (rotationAxis.Y * rotationAxis.Y * t + c) +
                       origXYZ.Z * (rotationAxis.Y * rotationAxis.Z * t - rotationAxis.X * s);

            double z = origXYZ.X * (rotationAxis.Z * rotationAxis.X * t - rotationAxis.Y * s) +
                       origXYZ.Y * (rotationAxis.Z * rotationAxis.Y * t + rotationAxis.X * s) +
                       origXYZ.Z * (rotationAxis.Z * rotationAxis.Z * t + c);

            return new XYZ(x, y, z);
        }

        /// <summary>
        /// 返回两个向量的叉积
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public XYZ CrossProduct(XYZ source)
        {
            XYZ origXYZ = new XYZ(X, Y, Z);
            double crossX = origXYZ.Y * source.Z - origXYZ.Z * source.Y;
            double crossY = origXYZ.Z * source.X - origXYZ.X * source.Z;
            double crossZ = origXYZ.X * source.Y - origXYZ.Y * source.X;

            return new XYZ(crossX, crossY, crossZ);
        }

        /// <summary>
        /// 返回向量点积
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public double DotProduct(XYZ source)
        {
            return this.X* source.X+this.Y*source.Y+this.Z*source.Z;
        }

        /// <summary>
        /// 返回向量的长度
        /// </summary>
        /// <returns></returns>
        public double Length()
        {
            return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2) + Math.Pow(Z, 2));
        }

        /// <summary>
        /// 保留小数四舍五入
        /// </summary>
        /// <param name="count">保留小数位数</param>
        /// <returns></returns>
        public XYZ RoundDecimal(int count=2)
        {
            double value= Math.Pow(10,count);
            return new XYZ(Math.Round(X * value) / value, Math.Round(Y * value) / value, Math.Round(Z * value) / value);
        }

        /// <summary>
        /// 当前向量减去目标向量得到新向量
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public XYZ Subtract(XYZ source)
        {
            return new XYZ(X- source.X, Y-source.Y, Z-source.Z);
        }

        /// <summary>
        /// 判断两个向量是否平行
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public bool IsParallel(XYZ source)
        {
            
            if(new XYZ(X, Y, Z).Normalize().Subtract(source.Normalize()).Length() < 1e-9)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 将点坐标转化为字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return X.ToString()+" "+Y.ToString()+" "+Z.ToString();
        }

        public XYZ(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        //
        // 摘要:
        //     Negates this vector.
        //
        // 返回结果:
        //     The vector opposite to this vector.
        //
        // 言论：
        //     The negated vector is obtained by changing the sign of each coordinate of this
        //     vector.
        public XYZ Negate()
        {
            double z = this.Z;
            double y = this.Y;
            double x = this.X;
            return new XYZ(0.0 - x, 0.0 - y, 0.0 - z);
        }

        public static XYZ RotateByAngleAroundZ(XYZ vector, double angle)
        {
            double cosTheta = Math.Cos(angle);
            double sinTheta = Math.Sin(angle);
            return new XYZ(
                cosTheta * vector.X - sinTheta * vector.Y,
                sinTheta * vector.X + cosTheta * vector.Y,
                vector.Z // 保持 Z 轴值不变
            );
        }
    }

}
